package services

import (
	"encoding/json"
	"fmt"
	"go.uber.org/zap"
	"io/ioutil"
	"net/http"
	"net/url"
	"purchase-mall/dao/redis"
	"purchase-mall/dao/ts"
	"purchase-mall/models"
	"purchase-mall/services/service"
	"purchase-mall/utils"
	"strconv"
	"sync"
	"time"
)

// GetRequestTop20Service 获取token请求数top20
func GetRequestTop20Service(topSearch *service.Top20Search) (data *models.Top20Info, err error) {
	var topNum int64 = 20
	var endTime int64
	var days int

	if topSearch.Filters.QuickDate != "" {
		switch topSearch.Filters.QuickDate {
		case "today":
			days = 1
			endTime = utils.TodayTime()
		case "yesterday":
			days = 1
			endTime = utils.YesterdayTime()
		case "week":
			days = 7
			endTime = utils.TodayTime()
		case "month":
			days = 30
			endTime = utils.TodayTime()
		case "year":
			days = 365
			endTime = utils.TodayTime()
		}
	} else if topSearch.Filters.Date != nil && len(topSearch.Filters.Date) == 2 {
		days = int(utils.GetTimeArr(topSearch.Filters.Date[0], topSearch.Filters.Date[1]))
		t, _ := time.ParseInLocation("2006-01-02 15:04:05", topSearch.Filters.Date[1], time.Local)
		endTime = t.UnixNano()
	} else {
		days = 1
		endTime = utils.TodayTime()
	}

	params := url.Values{}
	parseURL, err := url.Parse(utils.RequestGetUrlNoParams)
	if err != nil {
		return
	}
	params.Set("query", fmt.Sprintf("topk(%d,sum (count_over_time({filename=\"/apisix/log/error.log\",env=\"prod\",exporter=\"promtail\"} | json | token!=\"\" |json token = \"token\" | __error__!=\"JSONParserErr\"  [%dd])) by(token))", topNum, days))
	params.Set("time", strconv.FormatInt(endTime, 10))
	parseURL.RawQuery = params.Encode()
	urlPathWithParams := parseURL.String()

	client := http.Client{
		Timeout: 10 * time.Minute,
	}

	resp, err := client.Get(urlPathWithParams)
	if err != nil {
		zap.L().Error("Loki Http Api GetTop20Service failed",
			zap.Int64("time", endTime),
			zap.Error(err))
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	var res RespJson
	err = json.Unmarshal(body, &res)
	if err != nil {
		return
	}

	if res.Status != "success" {
		return
	}

	top20s := make([]*models.Top20, 0, len(res.Data.Result))
	data = &models.Top20Info{
		PageNum:  topSearch.PageNum,
		PageSize: topSearch.PageSize,
		Total:    int64(len(res.Data.Result)),
	}

	for _, value := range res.Data.Result {
		var requestNum int
		if value.Value != nil && len(value.Value) >= 2 {
			requestNum, _ = strconv.Atoi(value.Value[1].(string))
		}

		top20 := models.Top20{
			Token:      value.Metric.Token,
			Timestamp:  value.Value[0].(float64),
			RequestNum: int32(requestNum),
		}
		top20s = append(top20s, &top20)
	}

	BubbleSortTop20s(top20s)

	start := int((data.PageNum - 1) * data.PageSize)
	end := int(data.PageNum * data.PageSize)
	if start > len(top20s) {
		data.List = make([]*models.Top20, 0)
		return
	}
	if end > len(top20s) {
		data.List = top20s[start:len(top20s)]
		return
	}

	data.List = top20s[start:end]

	//userinfo
	var wg sync.WaitGroup
	wg.Add(len(data.List))
	for _, v := range data.List {
		go func(v *models.Top20) {
			defer wg.Done()
			if v.Token != "" {
				tokenInfos, err := GetTokenInfo(v.Token)
				if err == nil {
					if tokenInfos.ResponseJson != nil && len(tokenInfos.ResponseJson) > 0 {
						v.Level = tokenInfos.ResponseJson[0].TrafficLevel
						v.NickName = tokenInfos.ResponseJson[0].NickName
						v.Quota = tokenInfos.ResponseJson[0].Traffic.Quota
						v.Account = tokenInfos.ResponseJson[0].Account
					}
				}
				v.IsBlock = redis.IsBlock(v.Token)
			}
		}(v)
	}
	wg.Wait()

	return
}

//GetTokenListService 获取token列表
func GetTokenListService(tokenSearch *service.TokenListSearch) (data *models.Token, err error) {
	var endTime int64
	var days int

	if tokenSearch.Filters.QuickDate != "" {
		switch tokenSearch.Filters.QuickDate {
		case "today":
			days = 1
			endTime = utils.TodayTime()
		case "yesterday":
			days = 1
			endTime = utils.YesterdayTime()
		case "week":
			days = 7
			endTime = utils.TodayTime()
		case "month":
			days = 30
			endTime = utils.TodayTime()
		case "year":
			days = 365
			endTime = utils.TodayTime()
		}
	} else if tokenSearch.Filters.Date != nil && len(tokenSearch.Filters.Date) == 2 {
		days = int(utils.GetTimeArr(tokenSearch.Filters.Date[0], tokenSearch.Filters.Date[1]))
		t, _ := time.ParseInLocation("2006-01-02 15:04:05", tokenSearch.Filters.Date[1], time.Local)
		endTime = t.UnixNano()
	} else {
		days = 1
		endTime = utils.TodayTime()
	}

	params := url.Values{}
	parseURL, err := url.Parse(utils.RequestGetUrlNoParams)
	if err != nil {
		return
	}
	if tokenSearch.Filters.Token != "" {
		params.Set("query", fmt.Sprintf("sum by(token) (count_over_time({filename=\"/apisix/log/error.log\",from=\"access log\",env=\"prod\",exporter=\"promtail\"} | json | token!=\"\" | json | token=\"%s\" | json token = \"token\" | __error__!=\"JSONParserErr\" [%dd]))", tokenSearch.Filters.Token, days))
	} else {
		params.Set("query", fmt.Sprintf("sum by(token) (count_over_time({filename=\"/apisix/log/error.log\",from=\"access log\",env=\"prod\",exporter=\"promtail\"} | json | token!=\"\" | json token = \"token\" | __error__!=\"JSONParserErr\" [%dd]))", days))
	}
	params.Set("time", strconv.FormatInt(endTime, 10))
	parseURL.RawQuery = params.Encode()
	urlPathWithParams := parseURL.String()

	client := http.Client{
		Timeout: 10 * time.Minute,
	}

	resp, err := client.Get(urlPathWithParams)
	if err != nil {
		zap.L().Error("Loki Http Api GetTokenListService failed",
			zap.Int64("time", endTime),
			zap.Error(err))
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	var res RespJson
	err = json.Unmarshal(body, &res)
	if err != nil {
		return
	}

	if res.Status != "success" {
		return
	}

	tokenList := make([]*models.TokenList, 0, len(res.Data.Result))
	data = &models.Token{
		PageNum:  tokenSearch.PageNum,
		PageSize: tokenSearch.PageSize,
		Total:    int64(len(res.Data.Result)),
	}

	for _, value := range res.Data.Result {
		var requestNum int
		if value.Value != nil && len(value.Value) >= 2 {
			requestNum, _ = strconv.Atoi(value.Value[1].(string))
		}

		isBlock := redis.IsBlock(value.Metric.Token)

		var token = models.TokenList{
			Token:     value.Metric.Token,
			Timestamp: endTime,
			UseTotal:  int64(requestNum),
			IsBlock:   isBlock,
		}

		tokenList = append(tokenList, &token)
	}

	//sort
	BubbleSortTokens(tokenList)

	start := int((data.PageNum - 1) * data.PageSize)
	end := int(data.PageNum * data.PageSize)
	if start > len(tokenList) {
		data.List = make([]*models.TokenList, 0)
		return
	}
	if end > len(tokenList) {
		data.List = tokenList[start:len(tokenList)]
		return
	}

	data.List = tokenList[start:end]

	//userinfo
	var wg sync.WaitGroup
	wg.Add(len(data.List))
	for _, v := range data.List {
		go func(v *models.TokenList) {
			defer wg.Done()
			if v.Token != "" {
				tokenInfos, err := GetTokenInfo(v.Token)
				if err == nil {
					if tokenInfos.ResponseJson != nil && len(tokenInfos.ResponseJson) > 0 {
						v.Level = tokenInfos.ResponseJson[0].TrafficLevel
						v.NickName = tokenInfos.ResponseJson[0].NickName
						v.Quota = tokenInfos.ResponseJson[0].Traffic.Quota
						v.Account = tokenInfos.ResponseJson[0].Account
					}
				}
				v.IsBlock = redis.IsBlock(v.Token)
			}
		}(v)
	}
	wg.Wait()

	return
}

// GetTableList 获取table列表
func GetTableList(page, size int64) (data []*models.Table, err error) {
	tables, err := ts.GetTableList(page, size)
	if err != nil {
		return nil, err
	}
	//data = make([]*models.Table, 0, len(tokens))
	data = tables
	return
}

// GetTokenList 获取token列表
func GetTokenList(page, size int64, filters map[string]interface{}) (data *models.Token, err error) {
	fmt.Println(filters)
	data, err = ts.GetTokenList(page, size, filters)
	if err != nil {
		return nil, err
	}

	return
}
